#pragma once  // 防止头文件重复包含

// 必要的头文件包含
#include <iostream>     // 标准输入输出流
#include <time.h>      // 时间相关函数
#include <stdarg.h>    // 可变参数函数支持
#include <sys/types.h> // 系统调用相关类型
#include <sys/stat.h>  // 文件状态
#include <fcntl.h>     // 文件控制选项
#include <unistd.h>    // UNIX标准函数
#include <stdlib.h>    // 标准库函数

#define SIZE 1024  // 缓冲区大小

// 日志级别定义
#define Info 0     // 信息
#define Debug 1    // 调试
#define Warning 2  // 警告
#define Error 3    // 错误
#define Fatal 4    // 致命错误

// 日志输出方式定义
#define Screen 1     // 输出到屏幕
#define Onefile 2    // 输出到单个文件
#define Classfile 3  // 按日志级别分类输出到不同文件

#define LogFile "log.txt"  // 默认日志文件名

class Log
{
public:
    // 构造函数：初始化输出方式为屏幕输出，设置默认日志路径
    Log()
    {
        // 初始化日志输出方式为屏幕输出(Screen=1)
        // Screen：直接输出到终端屏幕
        // Onefile：输出到单个日志文件
        // Classfile：根据日志级别输出到不同文件
        printMethod = Screen;  

        // 设置日志文件存放的默认路径为当前目录下的log子目录
        // 注意:使用前需要确保该目录存在,否则写入文件会失败
        path = "./log/";      
    }

    // 设置日志输出方式的方法
    void Enable(int method)
    {
        // 通过传入不同的参数来修改日志的输出方式:
        // method可以是:
        // Screen(1) - 输出到屏幕
        // Onefile(2) - 输出到单个文件
        // Classfile(3) - 按日志级别分类输出到不同文件
        printMethod = method;
    }

    // 将日志级别转换为对应的字符串
    std::string levelToString(int level)
    {
        switch (level)
        {
        case Info:    return "Info";
        case Debug:   return "Debug";
        case Warning: return "Warning";
        case Error:   return "Error";
        case Fatal:   return "Fatal";
        default:      return "None";
        }
    }

    /* 注释掉的原始logmessage函数实现
    void logmessage(int level, const char *format, ...)
    {
        // 获取当前时间
        time_t t = time(nullptr);
        struct tm *ctime = localtime(&t);
        
        // 格式化日志头部（时间和级别信息）
        char leftbuffer[SIZE];
        snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", 
                levelToString(level).c_str(),
                ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,
                ctime->tm_hour, ctime->tm_min, ctime->tm_sec);

        // 处理可变参数
        char rightbuffer[SIZE];
        vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);

        // 组合完整日志消息
        char logtxt[SIZE * 2];
        snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);

        printLog(level, logtxt);
    }
    */

    // 根据不同的输出方式打印日志
    void printLog(int level, const std::string &logtxt)
    {
        switch (printMethod)
        {
        case Screen:     // 输出到屏幕
            std::cout << logtxt << std::endl;
            break;
        case Onefile:    // 输出到单个文件
            printOneFile(LogFile, logtxt);
            break;
        case Classfile:  // 按级别输出到不同文件
            printClassFile(level, logtxt);
            break;
        default:
            break;
        }
    }

    // 将日志写入指定文件
    void printOneFile(const std::string &logname, const std::string &logtxt)
    {
        std::string _logname = path + logname;
        // 打开文件,使用以下标志:
        // O_WRONLY: 只写模式
        // O_CREAT: 如果文件不存在则创建
        // O_APPEND: 追加写入,新内容添加到文件末尾
        // 0666: 文件权限(rw-rw-rw-)
        //fd用来标识一个打开的文件
        int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);
        if (fd < 0) // 如果打开文件失败(fd<0),直接返回
            return;
        // 将日志内容写入文件
        // logtxt.c_str(): 获取日志内容的C风格字符串
        // logtxt.size(): 获取日志内容的长度
        write(fd, logtxt.c_str(), logtxt.size());  // 使用fd写入文件
		close(fd);  // 使用fd关闭文件
    }

    // 根据日志级别将日志写入对应的文件
    void printClassFile(int level, const std::string &logtxt)
    {
        std::string filename = LogFile;
        filename += ".";
        filename += levelToString(level);  // 例如: "log.txt.Debug"
        printOneFile(filename, logtxt);
    }

    ~Log()
    {
    }

    // 重载operator()函数，实现日志打印功能
    // level: 日志级别
    // format: 格式化字符串
    // ...: 可变参数列表
    void operator()(int level, const char *format, ...)
    {
        // 1. 构造日志的左半部分：时间戳和日志级别
        time_t t = time(nullptr);          // 获取当前时间戳
        struct tm *ctime = localtime(&t);  // 转换为本地时间
        char leftbuffer[SIZE];             // 存储左半部分的缓冲区

        // 格式化左半部分：[级别][年-月-日 时:分:秒]
        /*
        int snprintf(char *buffer, size_t size, const char *format, ...);
        参数说明：
            buffer：输出缓冲区，用于存储格式化后的字符串
            size：缓冲区大小（字节数），包括结尾的空字符'\0'
            format：格式化字符串
            ...：可变参数列表
        */
        snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", 
                levelToString(level).c_str(),  // 日志级别转字符串
                ctime->tm_year + 1900,         // 年(需要加1900)
                ctime->tm_mon + 1,             // 月(需要加1)
                ctime->tm_mday,                // 日
                ctime->tm_hour,                // 时
                ctime->tm_min,                 // 分
                ctime->tm_sec);                // 秒

        // 2. 处理可变参数部分(日志内容)
        va_list s;                        // 定义可变参数列表
        va_start(s, format);              // 初始化可变参数列表
        char rightbuffer[SIZE];           // 存储右半部分的缓冲区
        vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);  // 格式化可变参数
        va_end(s);                        // 清理可变参数列表

        // 3. 组合完整的日志信息
        char logtxt[SIZE * 2];            // 存储完整日志的缓冲区
        snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);  // 合并左右部分

        // 4. 调用printLog函数输出日志
        printLog(level, logtxt);
    }

private:
    int printMethod;      // 日志输出方式
    std::string path;     // 日志文件路径
};

/* 注释掉的可变参数示例函数
int sum(int n, ...)
{
    va_list s;           // 定义可变参数列表
    va_start(s, n);      // 初始化可变参数列表

    int sum = 0;
    while(n)
    {
        sum += va_arg(s, int);  // 依次获取参数
        n--;
    }

    va_end(s);          // 清理可变参数列表
    return sum;
}
*/